package colibri.lcdui;

import colibri.App;
import colibri.util.ArrayList;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**Cria um grupo de botões.
 * @author Bayron Thiengo Quinelato
 */
class CommandHandler extends Item {

    private String labelLeft, labelRight;
    private boolean pressedLeft, pressedRight;
    private Font font, fontMenu;
    private int fontMenuH;
    private int colorMenuTextOut, colorMenuTextIn;
    private int colorTextOut, colorTextIn;
    private int labelX, labelY;
    private int backgrounColorOut, backgrounColorIn;
    private int borderColor;
    private int commmandBarH;
    // Commands
    private Command[] commands;
    int numCommands;
    private String[] labels;
    private boolean open;
    private int index;
    private int itemH, itemW;
    private ArrayList subCommands;
    private boolean openSB;
    private int numSubCommands;
    private int sbHeight;
    private int indexSB;
    private int sbItemW;
    private int sbX, sbY;

    public CommandHandler(Canvas canvas) {
        super(0, 0, 0, 0, Graphics.LEFT | Graphics.TOP);
        super.setCanvas(canvas);

        font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
        fontMenu = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
        fontMenuH = fontMenu.getHeight();

        open = false;
        pressedLeft = false;
        pressedRight = false;
        index = 0;
        backgrounColorOut = 0x3D3D3D;
        backgrounColorIn = 0xBEBABA;
        borderColor = 0xBEBABA;
        colorTextOut = 0xFFFFFF;
        colorTextIn = 0x666666;
        colorMenuTextOut = 0xFFFFFF;
        colorMenuTextIn = 0x666666;
        labelX = 5;
        labelY = 5;
        itemW = 0;
        itemH = 0;
        commmandBarH = labelY + font.getHeight() + 2;
        labelLeft = null;
        labelRight = null;
        itemH = fontMenuH + 10;
        indexSB = 0;
        openSB = false;
    }

    void updateCommandSet() {
        if (numCommands <= 0) {
            open = false;
            pressedLeft = false;
            pressedRight = false;
            index = 0;
            backgrounColorOut = 0x3D3D3D;
            backgrounColorIn = 0xBEBABA;
            borderColor = 0xBEBABA;
            colorTextOut = 0xFFFFFF;
            colorTextIn = 0x666666;
            labelX = 5;
            labelY = 5;
            itemW = 0;
            itemH = 0;
            labelLeft = null;
            labelRight = null;
            itemH = fontMenuH + 10;
            indexSB = 0;
            openSB = false;
            return;
        }

        labels = new String[numCommands];
        width = App.width;
        itemW = 0;
        for (int i = 0; i < numCommands; i++) {
            labels[i] = commands[i].getLabel();
            if (fontMenu.stringWidth(labels[i] + " >>") > itemW)
                itemW = fontMenu.stringWidth(labels[i] + " >>") + 10;
        }
        if (itemW < 60)
            itemW = 60;

        if (itemW > App.halfWidth)
            itemW = App.halfWidth;

        if (labelY + font.getHeight() + 2 > ((Form) getCanvas()).getFooterHeight())
            commmandBarH = labelY + font.getHeight() + 2;
        else
            commmandBarH = ((Form) getCanvas()).getFooterHeight();

        if (numCommands > 2)
            height = (numCommands - 1) * itemH + 4 + commmandBarH;
        else
            height = commmandBarH;

        y = App.height - height;

        labelRight = labels[0];
        if (numCommands == 1)
            labelLeft = null;
        else if (numCommands == 2)
            labelLeft = labels[1];
        else
            labelLeft = "Menu";
    }

    /**Gera a imagem do componente.*/
    protected void callPaint(Graphics g) {
        if (!visible || labels == null || numCommands <= 0) {
            return;
        }

        g.setFont(font);
        g.setColor(colorTextOut);

        if (labelRight != null) {
            if (pressedRight && !isOpen()) {
                g.setColor(colorTextIn);
            } else {
                g.setColor(colorTextOut);
            }
            g.drawString(labelRight, App.width - labelX, App.height - labelY, Graphics.BASELINE | Graphics.RIGHT);
        }

        if (labelLeft != null) {
            if (pressedLeft) {
                g.setColor(colorTextIn);
            } else {
                g.setColor(colorTextOut);
            }
            g.drawString(labelLeft, labelX, App.height - labelY, Graphics.BASELINE | Graphics.LEFT);
        }

        if (open) {
            g.setColor(backgrounColorOut);
            g.fillRect(x, y, itemW, height - commmandBarH);
            g.setColor(borderColor);
            g.drawRect(x, y, itemW - 1, height - commmandBarH - 1);

            int itemY = y + 2;
            g.setFont(fontMenu);

            for (int i = 1; i < numCommands; i++) {
                if (index == i) {
                    g.setColor(backgrounColorIn);
                    g.fillRect(2, itemY, itemW - 4, itemH);
                    g.setColor(colorMenuTextIn);
                } else {
                    g.setColor(colorMenuTextOut);
                }
                if (labels[i] != null) {
                    String lab = labels[i];
                    if (hasSubCommands(i)) {
                        lab += " >>";
                    }
                    g.drawString(lab, itemW / 2, itemY + (itemH - fontMenuH) / 2, Graphics.TOP | Graphics.HCENTER);
                }
                itemY += itemH;
            }

            if (openSB) {
                // Pega os subcmandos do comando selecionado
                Command[] cmd = (Command[]) subCommands.get(index);
                numSubCommands = cmd.length;
                // Verifica se existem subcomandos, senão sai
                if (cmd == null || numSubCommands <= 0) {
                    return;
                }
                // Calcula a largura máxima dos subcomandos
                sbItemW = 0;
                for (int i = 0; i < numSubCommands; i++) {
                    if (fontMenu.stringWidth(cmd[i].getLabel()) > sbItemW) {
                        sbItemW = fontMenu.stringWidth(cmd[i].getLabel()) + 10;
                    }
                }
                if (sbItemW < 60) {
                    sbItemW = 60;
                }
                if (sbItemW > App.halfWidth) {
                    sbItemW = App.halfWidth;
                }
                // Define a altura da lista de subcomandos
                sbHeight = (numSubCommands + 1) * itemH + 4;
                // Define o ponto x dos subcomandos
                sbX = x + itemW / 2;
                // Define o ponto y dos subcomandos
                if (numSubCommands < numCommands) {
                    sbY = y - itemH / 2;
                } else {
                    sbY = App.height - sbHeight;
                }
                // Desenha o plano de fundo dos subcomandos
                g.setColor(backgrounColorOut);
                g.fillRect(sbX, sbY, sbItemW, sbHeight);
                g.setColor(borderColor);
                g.drawRect(sbX, sbY, sbItemW - 1, sbHeight - 1);
                // Desenha os subcomandos
                itemY = sbY + 2;
                String sbLabel;
                for (int i = 0; i <= numSubCommands; i++) {
                    if (indexSB == i) {
                        g.setColor(backgrounColorIn);
                        g.fillRect(sbX + 2, itemY, sbItemW - 4, itemH);
                        g.setColor(colorMenuTextIn);
                    } else {
                        g.setColor(colorMenuTextOut);
                    }
                    sbLabel = "";
                    if (i < numSubCommands && cmd[i] != null) {
                        sbLabel = cmd[i].getLabel();
                    } else if (i == numSubCommands) {
                        sbLabel = "<<";
                    }
                    g.drawString(sbLabel, sbX + sbItemW / 2, itemY + (itemH - fontMenuH) / 2, Graphics.TOP | Graphics.HCENTER);
                    itemY += itemH;
                }
            }
        }
    }

    /**Sobe o menu.*/
    private final void up() {
        if (!visible || !open || labels == null || numCommands < 3) {
            return;
        }
        if (openSB) {
            if (indexSB > 0) {
                --indexSB;
            } else {
                indexSB = numSubCommands;
            }
        } else {
            if (index > 1) {
                --index;
            } else {
                index = numCommands - 1;
            }
        }
    }

    /**Desce o ButtonGroup.*/
    private void down() {
        if (!visible || !open || labels == null || numCommands < 3) {
            return;
        }
        if (openSB) {
            if (indexSB < numSubCommands) {
                indexSB++;
            } else {
                indexSB = 0;
            }
        } else {
            if (index < numCommands - 1) {
                index++;
            } else {
                index = 1;
            }
        }
    }

    void addSubCommand(Command cmd, Command subCmd) {
        // Se não tiver command sai
        if (commands == null || numCommands == 0) {
            return;
        }
        // Pega o índice do commando pai
        int indexCmd = -1;
        for (int i = 0; i < numCommands; i++) {
            if (cmd == commands[i]) {
                indexCmd = i;
                break;
            }
        }
        if (indexCmd < 0) {
            return;
        }
        // Se o array de subcomandos for nulo então inicializa-o
        if (subCommands == null) {
            subCommands = new ArrayList(numCommands);
        }
        if (subCommands.size() < numCommands) {
            for (int i = 0; i < numCommands; i++) {
                subCommands.add(new Command[0]);
            }
        }
        // Adiciona o subcomando
        Command[] subCmds = (Command[]) subCommands.get(indexCmd);
        if (subCmds == null) {
            subCmds = new Command[1];
            subCmds[0] = subCmd;
        } else {
            Command newSubCommands[] = new Command[subCmds.length + 1];
            if (subCmds != null) {
                System.arraycopy(subCmds, 0, newSubCommands, 0, subCmds.length);
            }
            subCmds = newSubCommands;
            subCmds[subCmds.length - 1] = subCmd;
        }
        subCommands.set(indexCmd, subCmds);
    }

    void removeSubCommands(Command cmd) {
        // Se não tiver command sai
        if (commands == null || numCommands == 0 || subCommands == null || subCommands.size() == 0) {
            return;
        }
        // Pega o índice do commando pai
        int indexCmd = -1;
        for (int i = 0; i < numCommands; i++) {
            if (cmd == commands[i]) {
                indexCmd = i;
                break;
            }
        }
        if (indexCmd < 0) {
            return;
        }
        // Remove os subcomandos
        subCommands.remove(indexCmd);
    }

    private boolean hasSubCommands(int index) {
        if (subCommands == null || numCommands < 3 || index == 0 || index >= subCommands.size()) {
            return false;
        }
        // Pega os subcmandos do comando selecionado
        Command[] cmd = (Command[]) subCommands.get(index);
        // Verifica se existem subcomandos, senão sai
        if (cmd == null || cmd.length <= 0) {
            return false;
        }
        return true;
    }

    void setLabelBounds(int labelX, int labelY) {
        this.labelX = labelX;
        this.labelY = labelY;
        updateCommandSet();
    }

    void setFontMenu(Font font) {
        if (font == null) {
            return;
        }
        fontMenu = font;
        fontMenuH = fontMenu.getHeight();
        itemH = fontMenuH + 10;
    }

    void setFont(Font font) {
        if (font == null) {
            return;
        }
        this.font = font;
    }

    /**Setter das cores do ButtonGroup.
     * @param backGroundColor Cor de fundo do ButtonGroup.
     * @param selectedButtonColor Cor do item selecionado do ButtonGroup.
     */
    void setBackgroundColor(int color1, int color2) {
        this.backgrounColorOut = color1;
        this.backgrounColorIn = color2;
        this.borderColor = color2;
    }

    void setTextColor(int coloroOut, int colorIn) {
        this.colorTextOut = coloroOut;
        this.colorTextIn = colorIn;
    }

    void setMenuTextColor(int coloroOut, int colorIn) {
        this.colorMenuTextOut = coloroOut;
        this.colorMenuTextIn = colorIn;
    }

    void open() {
        if (!open && labels != null && numCommands > 2) {
            open = true;
            index = 1;
        } else {
            open = false;
            index = 0;
        }
    }

    /**Abre os subcomandos do comando selecionado.*/
    void openSB() {
        if (!openSB && visible && numCommands > 2 && subCommands != null) {
            openSB = true;
        }
    }

    void close() {
        closeSB();
        open = false;
        index = 0;
    }

    /**Informa que o SubGroup do item selecionado não deve ser exibido.*/
    void closeSB() {
        openSB = false;
        indexSB = 0;
        numSubCommands = 0;
    }

    /**Informa se algum SubGroup está aberto.
     * @return true se tiver algum SubGroup aberto ou false se não tiver.
     */
    boolean isOpenSB() {
        return openSB;
    }

    boolean isOpen() {
        return open;
    }

    void addCommand(Command cmd) {
        for (int i = 0; i < numCommands; i++) {
            if (commands[i] == cmd) {
                return;
            }
        }
        if (commands == null || numCommands == commands.length) {
            Command newCommands[] = new Command[numCommands + 4];
            if (commands != null) {
                System.arraycopy(commands, 0, newCommands, 0, numCommands);
            }
            commands = newCommands;
        }
        commands[numCommands] = cmd;
        numCommands++;
        updateCommandSet();
    }

    void removeCommand(Command cmd) {
        int i = 0;
        do {
            if (i >= numCommands) {
                break;
            }
            if (commands[i] == cmd) {
                commands[i] = commands[--numCommands];
                commands[numCommands] = null;
                updateCommandSet();
                break;
            }
            i++;
        } while (true);
    }

    private void callAction(int index) {
        if (openSB) {
            if (index == numSubCommands) {
                closeSB();
            } else {
                CommandListener listener = ((Form) getCanvas()).getCommandListener();
                Command[] cmd = (Command[]) subCommands.get(this.index);
                listener.commandAction(cmd[index], getCanvas());
                cmd = null;
                close();
            }
        } else {
            if (hasSubCommands(index)) {
                openSB();
            } else {
                CommandListener listener = ((Form) getCanvas()).getCommandListener();
                listener.commandAction(commands[index], getCanvas());
                close();
            }
        }
    }

    protected void callKeyPressed(int keycode) {
        if (labels == null || !visible) {
            return;
        }
        if (App.isSoftKey_RIGHT(keycode)) {
            pressedLeft = false;
            pressedRight = true;
        } else if (App.isSoftKey_LEFT(keycode)) {
            pressedLeft = true;
            pressedRight = false;
        }
    }

    protected void callKeyReleased(int keyCode) {
        if (labels == null || !visible) {
            return;
        }
        pressedLeft = false;
        pressedRight = false;
        if (App.isSoftKey_RIGHT(keyCode)) {
            if (!open || numCommands < 3) {
                callAction(0);
            }
        } else if (App.isSoftKey_LEFT(keyCode)) {
            if (numCommands == 2) {
                callAction(1);
            } else if (numCommands > 2) {
                open();
            }
        }
        int gameAction = App.getGameAction(keyCode);
        switch (gameAction) {
            case App.FIRE:
                if (open && numCommands > 2) {
                    if (openSB) {
                        callAction(indexSB);
                    } else {
                        callAction(index);
                    }
                }
                break;
            case App.UP:
                up();
                break;
            case App.DOWN:
                down();
                break;
            case App.LEFT:
                if (openSB) {
                    closeSB();
                }
                break;
            case App.RIGHT:
                if (open && !openSB && hasSubCommands(index)) {
                    openSB();
                }
                break;
        }
    }

    protected void callKeyRepeated(int keyCode) {
        if (labels == null || !visible) {
            return;
        }
        if (labels != null && numCommands > 2) {
            int gameAction = App.getGameAction(keyCode);
            switch (gameAction) {
                case App.UP:
                    up();
                    break;
                case App.DOWN:
                    down();
                    break;
            }
        }
    }

    protected void callPointerPressed(int x, int y) {
        if (labels == null || !visible) {
            return;
        }
        if (labelLeft != null && x > this.x + labelX && x < this.x + labelX + font.stringWidth(labelLeft)
                && y > App.height - labelY - font.getHeight()) {
            pressedLeft = true;
            pressedRight = false;
        } else if (labelRight != null && x > App.width - labelX - font.stringWidth(labelRight) && x < App.width - labelX
                && y > App.height - labelY - font.getHeight()) {
            pressedLeft = false;
            pressedRight = true;
        } else if (open && numCommands > 2) {
            int _n;
            int _y;
            if (openSB) {
                if (x < sbX || x > sbX + sbItemW || y < sbY) {
                    return;
                }
                _n = numSubCommands;
                _y = sbY;
            } else {
                if (x < this.x || x > this.x + itemW || y < this.y) {
                    return;
                }
                _n = numCommands;
                _y = this.y;
            }
            for (int i = 0; i < _n; i++) {
                if (y > _y + 2 + i * itemH && y < _y + 2 + (i + 1) * itemH) {
                    if (openSB) {
                        indexSB = i;
                    } else {
                        index = i + 1;
                    }
                    break;
                }
            }
        }
    }

    protected void callPointerReleased(int x, int y) {
        if (labels == null || !visible) {
            return;
        }
        pressedLeft = false;
        pressedRight = false;
        if (labelLeft != null && x > this.x + labelX && x < this.x + labelX + font.stringWidth(labelLeft)
                && y > App.height - labelY - font.getHeight()) {
            if (numCommands == 2) {
                callAction(1);
            } else if (numCommands > 2) {
                open();
            }
        } else if (labelRight != null && x > App.width - labelX - font.stringWidth(labelRight) && x < App.width - labelX
                && y > App.height - labelY - font.getHeight()) {
            if (!open || numCommands < 3) {
                callAction(0);
            }
        } else if (open && numCommands > 2) {
            int _n;
            int _y;
            if (openSB) {
                if (x < sbX || x > sbX + sbItemW || y < sbY) {
                    return;
                }
                _n = numSubCommands + 1;
                _y = sbY;
            } else {
                if (x < this.x || x > this.x + itemW || y < this.y) {
                    return;
                }
                _n = numCommands;
                _y = this.y;
            }
            for (int i = 0; i < _n; i++) {
                if (y > _y + 2 + i * itemH && y < _y + 2 + (i + 1) * itemH) {
                    if (openSB) {
                        callAction(i);
                    } else {
                        callAction(i + 1);
                    }
                    break;
                }
            }
        }
    }

    protected void callPointerDragged(int x, int y) {
        pressedLeft = false;
        pressedRight = false;
    }
}
